﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ScrumTable.Common;
using ScrumTable.Common.FileSystem;
using ScrumTable.DL.Data.Common;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.Generic.Test.Mocks;

#endregion

namespace ScrumTable.DL.Data.Generic.Test.Cache
{
    /// <summary>
    /// This is the test class for CacheAttachment class.
    /// </summary>
    [TestClass]
    public class CacheAttachmentTest : CacheElementBaseTest<CacheAttachment>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        private CacheProject Project { get; set; }
        private CacheWorkItem UserStory { get; set; }
        #endregion

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new CacheAttachmentTest instance.
        /// </summary>
        public CacheAttachmentTest()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Use TestInitialize to run code before running each test .
        /// </summary>
        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();

            Project = CacheContext.Projects.TryGetById(Resources.Project1.Id);
            UserStory = Project.WorkItems.TryGetById(ResourceManager.UserStory1Id);
            ToTest = UserStory.Attachments.TryGetById(Resources.Attachment1.Id);
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run.
        /// </summary>
        [TestCleanup]
        public override void TestCleanup()
        {
            ToTest = null;
            base.TestCleanup();
        }

        [TestMethod]
        public void TestUri()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo newFile;

            using (originalFile.TemporaryCopy(out newFile))
            {
                ToTest.Uri = new Uri(newFile.FullName);
                Assert.AreEqual(new Uri(newFile.FullName), ToTest.Uri);
            }
        }

        [TestMethod]
        public void TestAddNewAttachment()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo file1ToAdd;
            FileInfo file2ToAdd;

            using (originalFile.TemporaryCopy(out file1ToAdd))
            {
                using (originalFile.TemporaryCopy(out file2ToAdd))
                {
                    CacheAttachment newAtta1 = CacheContext.ElementFactory.CreateAttachment(
                        file1ToAdd.Name,
                        CacheIdentity.DefaultId,
                        string.Empty,
                        new Uri(file1ToAdd.FullName));

                    CacheAttachment newAtta2 = CacheContext.ElementFactory.CreateAttachment(
                        file2ToAdd.Name,
                        CacheIdentity.DefaultId,
                        string.Empty,
                        new Uri(file2ToAdd.FullName));

                    UserStory.Attachments.Add(newAtta1);
                    UserStory.Attachments.Add(newAtta2);

                    Assert.IsTrue(UserStory.Attachments.Contains(newAtta1));
                    Assert.IsTrue(UserStory.Attachments.Contains(newAtta2));
                    Assert.IsTrue(UserStory.Attachments.IsDirty);
                    Assert.IsTrue(UserStory.IsDirty);
                }
            }
        }

        [TestMethod]
        public void TestUriWithOverwrite()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo fileToOverwrite;
            FileInfo fileOnEnd;

            using (originalFile.TemporaryCopy(out fileToOverwrite))
            {
                using (originalFile.TemporaryCopy(out fileOnEnd))
                {
                    ToTest.Uri = new Uri(fileToOverwrite.FullName);
                    ToTest.Uri = new Uri(fileOnEnd.FullName);

                    // fileToOverwrite will be deleted by the garbagge collector

                    fileOnEnd.Refresh();
                    Assert.IsTrue(fileOnEnd.Exists);
                }
            }
        }

        [TestMethod]
        public void TestUriWithSyncBackOverwrite()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo fileToOverwrite;
            FileInfo fileOnEnd;
            
            using (originalFile.TemporaryCopy(out fileToOverwrite))
            {
                using (originalFile.TemporaryCopy(out fileOnEnd))
                {
                    ToTest.Uri = new Uri(fileToOverwrite.FullName);

                    // data driver saves the attachment in meantime
                    CacheDataChanges changes = DataContext.ExportChanges();
                    changes.MarkChangesAs(CacheSavePairState.Saved);

                    // overwrite attachment in cache before changes sync back
                    ToTest.Uri = new Uri(fileOnEnd.FullName);

                    // try to retrieve saved element from backend system
                    DataContext.ImportChanges(changes);

                    // there must be only one attachment in cache
                    Assert.AreEqual(1, UserStory.Attachments.Count);
                }
            }
        }


        [TestMethod]
        public void TestUriWithTwiceOverwrite()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo fileToOverwrite;

            using (originalFile.TemporaryCopy(out fileToOverwrite))
            {
                ToTest.Uri = new Uri(fileToOverwrite.FullName);
                ToTest.Uri = new Uri(fileToOverwrite.FullName);

                // fileToOverwrite must still exist

                fileToOverwrite.Refresh();
                Assert.IsTrue(fileToOverwrite.Exists);
            }
        }

        [TestMethod]
        public void TestUriWithAccept()
        {
            Uri newServerUri = new Uri("http://www.example.com/");
            int newServerId = 21;

            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo tempFile;

            using (originalFile.TemporaryCopy(out tempFile))
            {
                Uri newUri = new Uri(tempFile.FullName);
                ToTest.Uri = newUri;
                Assert.IsTrue(UserStory.Attachments.IsDirty);

                CacheDataChanges changes = DataContext.ExportChanges();
                Assert.AreEqual(1, changes.ChangedWorkItems.Count);
                Assert.AreEqual(UserStory.UniqueId, changes.ChangedWorkItems.First().Value.Element.UniqueId);

                // emulate data driver: accept the changes and set the new server uri
                changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;

                CacheAttachment changedAttachment = changes.ChangedWorkItems.First().Value.Element.Attachments.TryGetById(ToTest.UniqueId);
                changedAttachment.Uri = newServerUri;
                changedAttachment.AssignId(newServerId);

                changes.RemoveDirtyFlags();
                DataContext.ImportChanges(changes);

                Assert.AreEqual(newServerUri, ToTest.Uri);
                Assert.AreEqual(newServerId, ToTest.Id);

                // temp files will be deleted by the garbagge collector
            }
        }

        [TestMethod]
        public void TestUriWithAcceptAndDlChange()
        {
            Uri newServerUri = new Uri("http://www.example.com/");

            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo tempFileBeforeSave;
            FileInfo tempFileDuringSave;

            using (originalFile.TemporaryCopy(out tempFileBeforeSave))
            {
                using (originalFile.TemporaryCopy(out tempFileDuringSave))
                {
                    ToTest.Uri = new Uri(tempFileBeforeSave.FullName);
                    CacheDataChanges changes = DataContext.ExportChanges();

                    // special case: a change occurred during the save process
                    Uri duringSaveUri = new Uri(tempFileDuringSave.FullName);
                    ToTest.Uri = duringSaveUri;

                    // emulate data driver: accept the changes and set the new server uri
                    changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;
                    changes.ChangedWorkItems.First().Value.Element.Attachments.TryGetById(Resources.Attachment1.Id).Uri = newServerUri;
                    changes.RemoveDirtyFlags();
                    DataContext.ImportChanges(changes);

                    // tempFileBeforeSave will be deleted by the garbagge collector

                    // check behaviour of CacheAttachment
                    tempFileDuringSave.Refresh();
                    Assert.IsTrue(tempFileDuringSave.Exists);
                    Assert.AreEqual(duringSaveUri, ToTest.Uri);
                }
            }
        }

        [TestMethod]
        public void TestUriWithReject()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo tempFile;

            using (originalFile.TemporaryCopy(out tempFile))
            {
                ToTest.Uri = new Uri(tempFile.FullName);
                CacheDataChanges changes = DataContext.ExportChanges();

                // emulate data driver: accept the changes and set the new server uri
                changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;
                changes.RemoveDirtyFlags();
                DataContext.ImportChanges(changes);

                // check behaviour of CacheAttachment
                tempFile.Refresh();
                Assert.IsTrue(tempFile.Exists);
            }
        }

        [TestMethod]
        public void TestUriWithRejectAndChange()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo tempFileBeforeSave;
            FileInfo tempFileDuringSave;

            using (originalFile.TemporaryCopy(out tempFileBeforeSave))
            {
                using (originalFile.TemporaryCopy(out tempFileDuringSave))
                {
                    ToTest.Uri = new Uri(tempFileBeforeSave.FullName);
                    CacheDataChanges changes = DataContext.ExportChanges();

                    // special case: a change occurred during the save process
                    Uri duringSaveUri = new Uri(tempFileDuringSave.FullName);
                    ToTest.Uri = duringSaveUri;

                    // emulate data driver: reject the changes
                    changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Error;
                    changes.RemoveDirtyFlags();
                    DataContext.ImportChanges(changes);

                    // tempFileBeforeSave will be deleted by the garbagge collector

                    // check behaviour of CacheAttachment
                    tempFileDuringSave.Refresh();
                    Assert.IsTrue(tempFileDuringSave.Exists);
                    Assert.AreEqual(duringSaveUri, ToTest.Uri);
                }
            }
        }

        [TestMethod]
        public void TestDeleteAttachmentUpdateFromServer()
        {
            CacheWorkItem firstWorkItem = Project.WorkItems.First();
            int oldAttachmentCount = firstWorkItem.Attachments.Count;

            CacheWorkItem workItemClone = (CacheWorkItem)firstWorkItem.Clone();
            CacheAttachment toDelete = workItemClone.Attachments.First();
            workItemClone.Attachments.Remove(toDelete);

            CacheDataChanges changes = DataContext.ExportChanges();
            changes.WorkItemsToSync[workItemClone.UniqueId] = new CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>>(
                new CacheElementListEntryBucket<CacheWorkItem>
                {
                    Element = workItemClone
                });


            // emulate data driver: accept the changes (the server side updated workitem contains a changed attachment)
            changes.WorkItemsToSync[workItemClone.UniqueId].State = CacheSavePairState.Updated;
            changes.RemoveDirtyFlags();

            // perpare event handling test
            DataAccessObjectEventTestStub eventTester = new DataAccessObjectEventTestStub(DataContext);
            eventTester.Action = DataAccessObjectChangedAction.Remove;
            eventTester.ElementId = toDelete.UniqueId;
            eventTester.RegisterEvents(DataContext.Schema.AttachmentSchema);

            try
            {
                DataContext.ImportChanges(changes);
            }
            finally
            {
                // unregister all events
                eventTester.UnregisterEvents();
            }

            // test event handling
            Assert.AreEqual(1, eventTester.ExpectedEventFireCount);
            Assert.AreEqual(0, eventTester.OtherEventFireCount);

            Assert.AreEqual(oldAttachmentCount - 1, firstWorkItem.Attachments.Count);
            Assert.IsFalse(firstWorkItem.Attachments.Contains(workItemClone.UniqueId));
        }

        [TestMethod]
        public void TestNewAttachmentFromServer()
        {
            FileInfo originalFile = new FileInfo(Resources.GetTestImageFilePath(TestContext.TestDir));
            FileInfo tempFileBeforeSave;
            FileInfo tempFileDuringSave;
            const int newAttaId = 10;

            using (originalFile.TemporaryCopy(out tempFileBeforeSave))
            {
                using (originalFile.TemporaryCopy(out tempFileDuringSave))
                {
                    ToTest.Uri = new Uri(tempFileBeforeSave.FullName);
                    CacheDataChanges changes = DataContext.ExportChanges();

                    // special case: in mean time there's a new attachment on server side
                    CacheAttachment newSeverAtta = CacheContext.ElementFactory.CreateAttachment(
                        tempFileDuringSave.Name,
                        newAttaId,
                        string.Empty,
                        new Uri(tempFileDuringSave.FullName));

                    // emulate data driver: accept the changes (a saved workitem contains a new attachment)
                    changes.WorkItemsToSync.First().Value.State = CacheSavePairState.Saved;
                    changes.WorkItemsToSync.First().Value.Bucket.Element.Attachments.Add(newSeverAtta);
                    changes.RemoveDirtyFlags();

                    // perpare event handling test
                    DataAccessObjectEventTestStub eventTester = new DataAccessObjectEventTestStub(DataContext);
                    eventTester.Action = DataAccessObjectChangedAction.Add;
                    eventTester.ElementId = newSeverAtta.UniqueId;
                    eventTester.RegisterEvents(DataContext.Schema.AttachmentSchema);

                    try
                    {
                        DataContext.ImportChanges(changes);
                    }
                    finally
                    {
                        // unregister all events
                        eventTester.UnregisterEvents();
                    }

                    // test event handling
                    Assert.AreEqual(1, eventTester.ExpectedEventFireCount);
                    Assert.AreEqual(0, eventTester.OtherEventFireCount);

                    // check merge changes behaviour, new attachment must be imported
                    bool imported = false;

                    foreach (CacheAttachment attachment in UserStory.Attachments)
                    {
                        if (attachment.Uri.ToLocalPath() == tempFileDuringSave.FullName)
                        {
                            Assert.AreEqual(newAttaId, attachment.Id);
                            imported = true;
                            break;
                        }
                    }
                    Assert.IsTrue(imported);
                }
            }
        }

        [TestMethod]
        [ExpectedException(typeof(FileNotFoundException))]
        public void TestUriWithException()
        {
            ToTest.Uri = new Uri("C:\\");
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
